package pl.us.ij.plugins.segmentation.infrastructure;

import ij.ImagePlus;
import ij.WindowManager;
import ij.process.ColorProcessor;
import ij.process.FloatProcessor;
import ij.process.ImageProcessor;
import pl.us.ij.plugins.segmentation.Trace;
import pl.us.ij.plugins.segmentation.infrastructure.exceptions.BreakCommandException;

public abstract class Command<T extends CommandParams> implements ICommand {
    private String name;
    private T params;
    private ImagePlus inputImage;

    public Command() {
        this.name = getClass().getSimpleName();
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void setImage(ImagePlus imp) {
        this.inputImage = imp;
        this.inputImage.unlock();
    }

    @Override
    public void setParams(CommandParams commandParams) {
        this.params = (T) commandParams;
    }

    @Override
    public void run() throws BreakCommandException {

        ImagePlus imp = prepareOutImage(this.inputImage);


        ConvertStrategy cs = prepareConvertStrategy(imp, this.params);

        Trace.log(cs.getClass().getSimpleName());

        for (int i = 0; i < cs.getChannelCount(); i++) {

            FloatProcessor fp = cs.getFloatProcessor(i);

            processImage(fp);

            cs.setFloatProcessor(i, fp);
        }

        if (finalizeOutImage(imp)) {
            imp.updateAndDraw();
            imp.show();
        }
    }

    protected T getParams() {
        return this.params;
    }

    protected float[] getCalibrationTable() {
        if (inputImage.getCalibration() == null)
            return null;

        return inputImage.getCalibration().getCTable();
    }

    protected ImagePlus prepareOutImage(ImagePlus inImage) {

        ImagePlus outImage = inImage.createImagePlus();
        ImageProcessor ip = inImage.getProcessor();

        String title = WindowManager.makeUniqueName(createOutImageTitle(inImage));

        inImage.trimProcessor();
        outImage.trimProcessor();

        outImage.setProcessor(title, ip.duplicate());
//        outImage.changes = true;
        outImage.setCalibration(inImage.getCalibration());

        return outImage;
    }

    protected ConvertStrategy prepareConvertStrategy(ImagePlus imp, CommandParams params) {
        ImageProcessor ip = imp.getProcessor();

        if (ip instanceof ColorProcessor) {
            return new ColorConvertStrategy(imp, params);
        }

        return new DefaultConvertStrategy(imp, params);
    }

    protected abstract void processImage(FloatProcessor fp) throws BreakCommandException;

    protected boolean finalizeOutImage(ImagePlus outImp) {
        return true;
    }

    protected String createOutImageTitle() {
        return createOutImageTitle(this.inputImage);
    }
    
    protected String createOutImageTitle(ImagePlus imp) {
        String title = imp.getTitle();

        int pos = title.lastIndexOf('.');

        String shortTitle = (pos != -1)
                ? title.substring(0, pos)
                : title;

        String extension = (pos != -1)
                ? title.substring(pos + 1)
                : "";

        return composeOutImageTitle(shortTitle, extension);
    }

    protected String composeOutImageTitle(String shortTitle, String extension) {
        return String.format("%s-%s.%s", shortTitle, getName().replace("Command", ""), extension);
    }
}

